
// ===============================================================================================================
// -*- C++ -*-
//
// MemoryBuffer.hpp - Declaration of a memory buffer class.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#ifndef __MEMORY_BUFFER_HPP__
#define __MEMORY_BUFFER_HPP__

// C++ std includes
#include <cstdlib>
#include <cstdio>
#include <string>

// Local includes
#include <RefPtr.hpp>
#include <ReferenceCountable.hpp>

///
/// Object containing information to control a stream of bytes in memory.
/// This type of object identifies a memory stream and contains the information needed to control it,
/// including a pointer to its buffer, its position indicator and all its state indicators.
///
/// The MemoryBuffer class is reference counted and cloneable.
///
/// \note The buffer does NOT resize when full.
///
class MemoryBuffer : public ReferenceCountable {

public:

	// Static Methods:

	/// Creates a new instance of the MemoryBuffer class.
	static MemoryBuffer * Create(size_t bufferSizeInBytes);

	/// Creates a new instance of the MemoryBuffer class and load an entire file into it. (File must exist)
	static MemoryBuffer * CreateFromFileData(const std::string & fileName);

	// Public Interface:

	/// Reads a given number of bytes from the buffer. (No endian swap performed)
	/// \param [out] destBuf: A buffer with a size equal to at least numBytesToRead, to hold the data read.
	/// \param [in] numBytesToRead: Number of bytes to copy from the stream to destBuf.
	bool ReadBytes(void * destBuf, size_t numBytesToRead);

	/// Writes a given number of bytes to the buffer. (No endian swap performed)
	/// \param [in] srcBuf: Pointer to the array of elements to be written.
	/// \param [in] numBytesToWrite: Number of bytes to copy from srcBuf to the stream.
	bool WriteBytes(const void * srcBuf, size_t numBytesToWrite);

	/// Writes the string pointed by srcBuf to the buffer.
	/// The function begins copying from the address specified (srcBuf) until it reaches the terminating null character ('\0').
	/// This final null-character is NOT copied to the buffer.
	/// \param [in] srcBuf: An array containing the null-terminated sequence of characters to be written.
	bool WriteString(const char * srcBuf);

	/// Writes a character to the buffer and advances the position indicator.
	/// The character is written at the current position of the stream as indicated by the internal position indicator, which is then advanced one character.
	bool WriteChar(char c);

	/// Reads characters from the stream and stores them as a C string into destBuf until (maxCount-1) characters have been read
	/// or either a '\n' is found or the End-Of-Stream is reached, whichever comes first.
	/// A delimiter character makes ReadString() stop reading, but it is considered a valid character and therefore it is included in the string copied to destBuf.
	/// A null character is automatically appended in destBuf after the characters read to signal the end of the C string.
	/// \param [out] destBuf: Pointer to an array of chars where the string read is stored.
	/// \param [in] maxCount: Maximum number of characters to be read (including the final null-character).
	bool ReadString(char * destBuf, size_t maxCount, char delim = '\n');

	/// Reads the character currently pointed by the internal position indicator of the stream.
	/// The internal position indicator is then advanced by one character to point to the next one.
	bool ReadChar(char & c);

	/// Sets the position indicator associated with the stream to a new position
	/// defined by adding offset to a reference position specified by origin.
	/// \param [in] offset: Number of bytes to offset from origin.
	/// \param [in] origin: Position from where offset is added.
	long SeekPos(long offset, int origin);

	/// Returns the current value of the position indicator of the stream.
	/// \return On success, the current value of the position indicator is returned. If an error occurs, -1 is returned.
	long TellPos(void) const;

	/// Returns the size in bytes of the buffer.
	size_t GetSize(void) const;

	/// Checks whether the End-Of-Stream indicator associated with the stream is set.
	bool EndOfStream(void) const;

	/// Checks if the buffer is in a bad state for IO operations.
	bool Fail(void) const;

	/// Returns a pointer to the first byte of the internal buffer. Never delete this memory!
	void * GetBufferPointer(void) const;

	/// Retruns a clone of the memory buffer.
	MemoryBuffer * Clone(void) const;

	// ReferenceCountable Methods:

	virtual unsigned long AddRef(void) const;
	virtual unsigned long Release(void) const;
	virtual unsigned long ReferenceCount(void) const;

protected:

	virtual ~MemoryBuffer(void);

private:

	// This will prevent the class from being staticaly allocated.
	// The only way to create an instance of it now is thru the MemoryBuffer::Create() method.
	explicit MemoryBuffer(size_t bufferSize = 0);

	// Disable copy.
	MemoryBuffer(const MemoryBuffer &);
	MemoryBuffer & operator = (const MemoryBuffer &);

private:

	void * ptr;
	size_t size;
	size_t position;
};

///
/// Reference countable memory buffer pointer type.
///
typedef RefPtr<MemoryBuffer> MemoryBufferPtr;

#endif // __MEMORY_BUFFER_HPP__